package com.zg.common.core.service.support;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.zg.common.core.dao.ConditionLink;
import com.zg.common.core.dao.ConditionType;
import com.zg.common.core.dao.QueryCondition;
import com.zg.common.core.util.DateUtil;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

public class QueryWrapperBuilder {

    private static final Map<Class<?>, Map<String, SFunction<?, ?>>> CACHE = new ConcurrentHashMap<>();

    /**
     * 根据注解构建查询条件
     * @param entity 查询条件对象
     * @param clazz 实体类类型
     * @return LambdaQueryWrapper
     */
    public static <T> LambdaQueryWrapper<T> build(Object entity, Class<T> clazz) {
        LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
        if (entity == null) {
            return wrapper;
        }

        for (Field field : entity.getClass().getDeclaredFields()) {
            QueryCondition annotation = field.getAnnotation(QueryCondition.class);
            if (annotation == null) {
                continue;
            }

            try {
                field.setAccessible(true);
                Object value = field.get(entity);

                // 处理空值忽略
                if (annotation.ignoreEmpty() && isEmpty(value)) {
                    continue;
                }

                // 获取列名（优先使用注解配置）
                String columnName = StringUtils.isNotBlank(annotation.filed()) ?
                        annotation.filed() :
                        field.getName();

                // 构建条件表达式
                Consumer<LambdaQueryWrapper<T>> condition = buildCondition(
                        clazz, columnName, value, annotation.type(), annotation.link());

                // 处理条件连接符
                if (annotation.link() == ConditionLink.OR) {
                    wrapper.or(w -> condition.accept(w));
                } else {
                    condition.accept(wrapper);
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Reflection access error", e);
            }
        }
        return wrapper;
    }

    /**
     * 构建具体查询条件
     */
    private static <T> Consumer<LambdaQueryWrapper<T>> buildCondition(
            Class<T> clazz, String columnName, Object value,
            ConditionType type, ConditionLink link) {
        return wrapper -> {
            SFunction<T, ?> column = getColumn(clazz, columnName);
            if (column == null) {
                return;
            }

            switch (type) {
                case EQ:
                    wrapper.eq(column, value);
                    break;
                case NE:
                    wrapper.ne(column, value);
                    break;
                case LIKE:
                    wrapper.like(column, value);
                    break;
                case GT:
                    wrapper.gt(column, value);
                    break;
                case LT:
                    wrapper.lt(column, value);
                    break;
                case GE:
                    wrapper.ge(column, value);
                    break;
                case LE:
                    wrapper.le(column, value);
                    break;
                case IN:
                    if (value instanceof Collection) {
                        wrapper.in(column, (Collection<?>) value);
                    } else if (value != null && value.getClass().isArray()) {
                        wrapper.in(column, (Object[]) value);
                    }
                    break;
                case NOT_IN:
                    if (value instanceof Collection) {
                        wrapper.notIn(column, (Collection<?>) value);
                    } else if (value != null && value.getClass().isArray()) {
                        wrapper.notIn(column, (Object[]) value);
                    }
                    break;
                case BETWEEN:
                    if(value instanceof Date[]&& ((Object[]) value).length == 2){
                        Date[] range = (Date[]) value;
                        range[1]= DateUtil.getToEndOfDay(range[1]);
                        wrapper.between(column, range[0], range[1]);
                    } else if (value instanceof Object[] && ((Object[]) value).length == 2) {
                        Object[] range = (Object[]) value;
                        wrapper.between(column, range[0], range[1]);
                    }
                    break;
                default:
                    wrapper.eq(column, value);
            }
        };
    }

    /**
     * 根据字段名获取SFunction
     */
    private static <T> SFunction<T, ?> getColumn(Class<T> clazz, String fieldName) {
        // 先尝试从缓存获取
        Map<String, SFunction<?, ?>> classCache = CACHE.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>());
        if (classCache.containsKey(fieldName)) {
            return (SFunction<T, ?>) classCache.get(fieldName);
        }

        // 尝试通过LambdaUtils生成
        try {
            // 将下划线命名转回驼峰
//            String fieldName = StringUtils.underlineToCamel(columnName);

            SFunction<T, ?> function = MyLambdaUtils.getFunction(clazz, fieldName);
            classCache.put(fieldName, function);
            return function;
        } catch (Exception e) {
            throw new RuntimeException("Failed to create lambda for column: " + fieldName, e);
        }
    }

    /**
     * 判断是否为空值
     */
    private static boolean isEmpty(Object value) {
        if (value == null) {
            return true;
        }
        if (value instanceof CharSequence) {
            return ((CharSequence) value).length() == 0;
        }
        if (value instanceof Collection) {
            return ((Collection<?>) value).isEmpty();
        }
        if (value.getClass().isArray()) {
            return ((Object[]) value).length == 0;
        }
        return false;
    }

}
